/****************************************************************************
 *
 * This file is part of the CodeMax editor support code.
 *
 * Primary author: Christopher J. Cason.
 *
 * from Persistence of Vision(tm) Ray Tracer version 3.6.
 * Copyright 1991-2003 Persistence of Vision Team
 * Copyright 2003-2004 Persistence of Vision Raytracer Pty. Ltd.
 *---------------------------------------------------------------------------
 * NOTICE: This source code file is provided so that users may experiment
 * with enhancements to POV-Ray and to port the software to platforms other
 * than those supported by the POV-Ray developers. There are strict rules
 * regarding how you are permitted to use this file. These rules are contained
 * in the distribution and derivative versions licenses which should have been
 * provided with this file.
 *
 * These licences may be found online, linked from the end-user license
 * agreement that is located at http://www.povray.org/povlegal.html
 *---------------------------------------------------------------------------
 * This program is based on the popular DKB raytracer version 2.12.
 * DKBTrace was originally written by David K. Buck.
 * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
 *---------------------------------------------------------------------------
 * Thanks to WinMain Software (http://www.winmain.com/) for providing the (at
 * the time commercial) CodeMax edit control which the POVWIN editor is based.
 *---------------------------------------------------------------------------
 * $File: //depot/misc/win-sourcedist/codemax/component/TCodeMax.cpp $
 * $Revision: #1 $
 * $Change: 2944 $
 * $DateTime: 2004/07/05 13:11:12 $
 * $Author: chrisc $
 * $Log$
 *****************************************************************************/

#include <vcl.h>
#pragma hdrstop

#include <commctrl.h>
#define CODEMAX_CPP
#include "TCodeMax.h"
#pragma package(smart_init)

static char LanguageNames [] [32] = {"", "C/C++", "Basic", "Java", "Pascal", "SQL", "POV-Ray", "HTML", "XML"} ;

//---------------------------------------------------------------------------
// ValidCtrCheck is used to assure that the components created do not have
// any pure virtual functions.
//

static inline void ValidCtrCheck (TCodeMax *)
{
  new TCodeMax(NULL);
}

//---------------------------------------------------------------------------
__fastcall TCodeMax::TCodeMax (TComponent* Owner) : TWinControl (Owner)
{
  FOpened = false ;
  FFileName = "" ;
  FLanguage = "" ;
  FIndex = 0 ;
  FBackedUp = false ;
  FShortName = "" ;
  FRMBDownX = FRMBDownY = 0 ;
  FRMBDownLine = FRMBDownCol = -1 ;
  FLButtonDown = false ;
}

//---------------------------------------------------------------------------
namespace Tcodemax
{
  void __fastcall PACKAGE Register()
  {
    TComponentClass classes[1] = {__classid(TCodeMax)};
    RegisterComponents("Misc", classes, 0);
  }
}
//---------------------------------------------------------------------------
void __fastcall TCodeMax::CreateParams (TCreateParams &Params)
{
  CMRegisterControl (CM_VERSION) ;
  TWinControl::CreateParams (Params) ;
  Params.Style = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS ;
  CreateSubClass (Params, "CodeMax") ;
}

void __fastcall TCodeMax::SetFileName (const System::AnsiString Filename)
{
  if (Filename == FFileName)
    return ;
  FOpened = false ;
  FFileName = Filename ;
}

void __fastcall TCodeMax::SetOpened (bool Opened)
{
  if (!Opened)
  {
    FOpened = false ;
  }
  else
    FOpened = CM_OpenFile (Handle, FFileName.c_str ()) ;
}

System::AnsiString __fastcall TCodeMax::GetLanguageName (void)
{
  char        name [256] ;

  CM_GetLanguage (Handle, name) ;
  FLanguage = name ;
  return (FLanguage) ;
}

TLanguage __fastcall TCodeMax::GetLanguage (void)
{
  int         i ;
  char        name [256] = "" ;

  CM_GetLanguage (Handle, name) ;
  for (i = 0 ; i < sizeof (LanguageNames) / sizeof (LanguageNames [0]) ; i++)
    if (stricmp (name, LanguageNames [i]) == 0)
      return (TLanguage (i)) ;
  return (cmlNone) ;
}

void __fastcall TCodeMax::SetBooleanValue (int message, bool value)
{
  SendMessage (Handle, message, value ? 1 : 0, 0) ;
}

bool __fastcall TCodeMax::GetBooleanValue (int message)
{
  switch (message)
  {
    case CMM_ENABLECOLORSYNTAX :
         message = CMM_ISCOLORSYNTAXENABLED ;
         break ;

    case CMM_ENABLEWHITESPACEDISPLAY :
         message = CMM_ISWHITESPACEDISPLAYENABLED ;
         break ;

    case CMM_ENABLETABEXPAND :
         message = CMM_ISTABEXPANDENABLED ;
         break ;

    case CMM_ENABLESMOOTHSCROLLING :
         message = CMM_ISSMOOTHSCROLLINGENABLED ;
         break ;

    case CMM_SETREADONLY :
         message = CMM_ISREADONLY ;
         break ;

    case CMM_ENABLELINETOOLTIPS :
         message = CMM_ISLINETOOLTIPSENABLED ;
         break ;

    case CMM_ENABLELEFTMARGIN :
         message = CMM_ISLEFTMARGINENABLED ;
         break ;

    case CMM_SETMODIFIED :
         message = CMM_ISMODIFIED ;
         break ;

    case CMM_ENABLEOVERTYPE :
         message = CMM_ISOVERTYPEENABLED ;
         break ;

    case CMM_ENABLECASESENSITIVE :
         message = CMM_ISCASESENSITIVEENABLED ;
         break ;

    case CMM_ENABLEPRESERVECASE :
         message = CMM_ISPRESERVECASEENABLED ;
         break ;

    case CMM_ENABLEWHOLEWORD :
         message = CMM_ISWHOLEWORDENABLED ;
         break ;

    case CMM_ENABLEDRAGDROP :
         message = CMM_ISDRAGDROPENABLED ;
         break ;

    case CMM_ENABLESPLITTER :
         message = CMM_ISSPLITTERENABLED ;
         break ;

    case CMM_ENABLECOLUMNSEL :
         message = CMM_ISCOLUMNSELENABLED ;
         break ;

    case CMM_ENABLEGLOBALPROPS :
         message = CMM_ISGLOBALPROPSENABLED ;
         break ;

    case CMM_ENABLECRLF :
         message = CMM_ISCRLFENABLED ;
         break ;

    case CMM_ENABLEREGEXP :
         message = CMM_ISREGEXPENABLED ;
         break ;

    case CMM_ENABLEOVERTYPECARET :
         message = CMM_ISOVERTYPECARETENABLED ;
         break ;

    case CMM_ENABLESELBOUNDS :
         message = CMM_ISSELBOUNDSENABLED ;
         break ;

  }
  return (SendMessage (Handle, message, 0, 0) != 0) ;
}

void __fastcall TCodeMax::SetIntegerValue (int message, int value)
{
  SendMessage (Handle, message, value, 0) ;
}

int __fastcall TCodeMax::GetIntegerValue (int message)
{
  switch (message)
  {
    case CMM_SETTABSIZE :
         message = CMM_GETTABSIZE ;
         break ;

    case CMM_SETUNDOLIMIT :
         message = CMM_GETUNDOLIMIT ;
         break ;

    case CMM_SETHIGHLIGHTEDLINE :
         message = CMM_GETHIGHLIGHTEDLINE ;
         break ;
  }
  return (SendMessage (Handle, message, 0, 0)) ;
}

TScrollStyle __fastcall TCodeMax::GetScrollBars (void)
{
  TScrollStyle          result = ssNone ;

  if (CM_HasScrollBar (Handle, true))
    result = ssHorizontal ;
  if (CM_HasScrollBar (Handle, false))
    result = result == ssNone ? ssVertical : ssBoth ;
  return (result) ;
}

void __fastcall TCodeMax::SetScrollBars (TScrollStyle style)
{
  switch (style)
  {
    case ssNone :
         CM_ShowScrollBar (Handle, false, false) ;
         CM_ShowScrollBar (Handle, true, false) ;
         break ;

    case ssHorizontal :
         CM_ShowScrollBar (Handle, false, false) ;
         CM_ShowScrollBar (Handle, true, true) ;
         break ;

    case ssVertical :
         CM_ShowScrollBar (Handle, false, true) ;
         CM_ShowScrollBar (Handle, true, false) ;
         break ;

    case ssBoth :
         CM_ShowScrollBar (Handle, false, true) ;
         CM_ShowScrollBar (Handle, true, true) ;
         break ;
  }
}

bool TCodeMax::GetText (AnsiString& Text, const CM_RANGE *pRange)
{
  int                   len = GetTextLength (pRange) ;

  if (len != -1)
  {
    char *buffer = new char [len + 1] ;
    CM_GetText (Handle, buffer, pRange) ;
    Text = buffer ;
    delete buffer ;
    return (true) ;
  }
  return (false) ;
}

System::AnsiString __fastcall TCodeMax::GetLine (int nLine)
{
  int                   len = LineLength [nLine] ;
  AnsiString            Line = "(error getting line)" ;

  if (len != -1)
  {
    char *buffer = new char [len + 1] ;
    GetLine (nLine, buffer) ;
    Line = buffer ;
    delete buffer ;
  }
  return (Line) ;
}

System::AnsiString TCodeMax::GetWord (CM_POSITION *pPos)
{
  int                   len = GetWordLength (pPos) ;
  AnsiString            Word = "(error getting word)" ;

  if (len != -1)
  {
    char *buffer = new char [len + 1] ;
    GetWord (buffer, pPos) ;
    Word = buffer ;
    delete buffer ;
  }
  return (Word) ;
}

CME_CODE TCodeMax::SetHotKeys (char *HotKeys)
{
  return (CMSetHotKeys ((unsigned char *) HotKeys)) ;
}

CME_CODE TCodeMax::SetMacro (int Index, char *Macro)
{
  return (CMSetMacro (Index, (unsigned char *) Macro)) ;
}

void TCodeMax::SetFindReplaceMRUList (AnsiString List, bool IsFind)
{
  CMSetFindReplaceMRUList ((char *) List.c_str (), IsFind) ;
}

int TCodeMax::GetHotKeys (char *HotKeys)
{
  return (CMGetHotKeys ((unsigned char *) HotKeys)) ;
}

int TCodeMax::GetMacro (int Index, char *Macro)
{
  return (CMGetMacro (Index, (unsigned char *) Macro)) ;
}

void TCodeMax::GetFindReplaceMRUList (AnsiString& List, bool IsFind)
{
  char                  buffer [CM_FIND_REPLACE_MRU_BUFF_SIZE] ;

  CMGetFindReplaceMRUList (buffer, IsFind) ;
  List = buffer ;
}

int __fastcall TCodeMax::GetLineNo (void)
{
  CM_RANGE              range ;

  CM_GetSel (Handle, &range, false) ;
  return (++range.posEnd.nLine) ;
}

void __fastcall TCodeMax::SetLineNo (int LineNo)
{
  CM_RANGE              range ;

  if (LineNo == 0)
    LineNo++ ;
  CM_GetSel (Handle, &range, false) ;
  range.posStart.nLine = --LineNo ;
  range.posEnd = range.posStart ;
  CM_SetSel (Handle, &range, true) ;
}

int __fastcall TCodeMax::GetColNo (void)
{
  CM_RANGE              range ;

  CM_GetSel (Handle, &range, false) ;
  return (++range.posEnd.nCol) ;
}

void __fastcall TCodeMax::SetColNo (int ColNo)
{
  CM_RANGE              range ;

  if (ColNo == 0)
    ColNo++ ;
  CM_GetSel (Handle, &range, false) ;
  range.posStart.nCol = --ColNo ;
  range.posEnd = range.posStart ;
  CM_SetSel (Handle, &range, true) ;
}

void __fastcall TCodeMax::SetPosition (int LineNo, int ColNo)
{
  CM_RANGE              range ;

  if (LineNo == 0)
    LineNo++ ;
  if (ColNo == 0)
    ColNo++ ;
  range.posStart.nLine = --LineNo ;
  range.posStart.nCol = --ColNo ;
  range.posEnd = range.posStart ;
  CM_SetSel (Handle, &range, true) ;
}

void TCodeMax::GetPosition (CM_POSITION *Position)
{
  CM_RANGE              range ;

  CM_GetSel (Handle, &range, false) ;
  *Position = range.posEnd ;
}

void TCodeMax::SetPosition (const CM_POSITION *Position)
{
  CM_RANGE              range ;

  range.posStart = range.posEnd = *Position ;
  CM_SetSel (Handle, &range, true) ;
}

void __fastcall TCodeMax::SetLanguage (TLanguage Language)
{
  CM_SetLanguage (Handle, LanguageNames [Language]) ;
}

void __fastcall TCodeMax::WndProc (Messages::TMessage &Message)
{
  CM_POSITION           pos ;

  if (Message.Msg == WM_LBUTTONDOWN)
  {
    FLButtonDown = true ;
    TWinControl::WndProc (Message) ;
    FLButtonDown = false ;
    return ;
  }
  if (Message.Msg == WM_RBUTTONDOWN)
  {
    Message.Result = 0 ;
    FRMBDownX = Message.LParamLo ;
    FRMBDownY = Message.LParamHi ;
    if (SendMessage (Handle, CMM_GETSELFROMPOINT, Message.LParam, (LPARAM) &pos) == CME_SUCCESS)
    {
      FRMBDownLine = pos.nLine + 1 ;
      FRMBDownCol = pos.nCol + 1 ;
    }
    else
      FRMBDownLine = FRMBDownCol = -1 ;
    // don't pass it on (so the caret doesn't move)
    return ;
  }
  if (Message.Msg == WM_ERASEBKGND)
  {
    Message.Result = 0 ;
    return ;
  }
  TWinControl::WndProc (Message) ;
}

//---------------------------------------------------------------------------
AnsiString TCodeMax::GetHotKeyString (CM_HOTKEY &cmHotKey)
{
  AnsiString            str ;
  UINT                  nVirtKey = cmHotKey.nVirtKey1 ;
  BYTE                  byModifiers = cmHotKey.byModifiers1 ;
  BYTE                  byOrigModifiers = byModifiers ;

  for (int i = 0 ; nVirtKey && (i < 2) ; i++)
  {
    if (i == 0 || (i != 0 && byModifiers != byOrigModifiers))
    {
      if ((byModifiers & HOTKEYF_CONTROL) == HOTKEYF_CONTROL)
        str += "Ctrl + " ;
      if ((byModifiers & HOTKEYF_SHIFT) == HOTKEYF_SHIFT)
        str += "Shift + " ;
      if ((byModifiers & HOTKEYF_ALT) == HOTKEYF_ALT)
        str += "Alt + " ;
    }

    if (nVirtKey)
    {
      LPTSTR pszChar ;
      char szTemp [2] ;

      switch (nVirtKey)
      {
        case VK_NUMLOCK:        pszChar = "Lock";           break;
        case VK_BACK:           pszChar = "Backspace";      break;
        case VK_INSERT:         pszChar = "Insert";         break;
        case VK_DELETE:         pszChar = "Delete";         break;
        case VK_HOME:           pszChar = "Home";           break;
        case VK_END:            pszChar = "End";            break;
        case VK_PRIOR:          pszChar = "Page Up";        break;
        case VK_NEXT:           pszChar = "Page Down";      break;
        case VK_LEFT:           pszChar = "Left";           break;
        case VK_RIGHT:          pszChar = "Right";          break;
        case VK_UP:             pszChar = "Up";             break;
        case VK_DOWN:           pszChar = "Down";           break;
        case VK_SCROLL:         pszChar = "Scroll Lock";    break;
        case VK_TAB:            pszChar = "Tab";            break;
        case VK_ESCAPE:         pszChar = "Esc";            break;
        case VK_RETURN:         pszChar = "Enter";          break;
        case VK_F1:             pszChar = "F1";             break;
        case VK_F2:             pszChar = "F2";             break;
        case VK_F3:             pszChar = "F3";             break;
        case VK_F4:             pszChar = "F4";             break;
        case VK_F5:             pszChar = "F5";             break;
        case VK_F6:             pszChar = "F6";             break;
        case VK_F7:             pszChar = "F7";             break;
        case VK_F8:             pszChar = "F8";             break;
        case VK_F9:             pszChar = "F9";             break;
        case VK_F10:            pszChar = "F10";            break;
        case VK_F11:            pszChar = "F11";            break;
        case VK_F12:            pszChar = "F12";            break;
        case VK_SPACE:          pszChar = "Space";          break;
        case VK_ADD:            pszChar = "Plus";           break;
        case 0x3b:              pszChar = "Plus";           break;
        case 0xbd:              pszChar = "Minus";          break;
        case VK_SUBTRACT:       pszChar = "Minus";          break;
        case 0x3d:              pszChar = "Minus";          break;

        default:
        {
          if (nVirtKey >= 0x60 && nVirtKey <= 0x6f)
            str += "Num " ;
          switch (nVirtKey)
          {
            case 0xc0:          { nVirtKey = '`'; break; }
            case 0x30:          { nVirtKey = '0'; break; }
            case 0x31:          { nVirtKey = '1'; break; }
            case 0x32:          { nVirtKey = '2'; break; }
            case 0x33:          { nVirtKey = '3'; break; }
            case 0x34:          { nVirtKey = '4'; break; }
            case 0x35:          { nVirtKey = '5'; break; }
            case 0x36:          { nVirtKey = '6'; break; }
            case 0x37:          { nVirtKey = '7'; break; }
            case 0x38:          { nVirtKey = '8'; break; }
            case 0x39:          { nVirtKey = '9'; break; }
            case 0xbb:          { nVirtKey = '='; break; }
            case 0xdb:          { nVirtKey = '['; break; }
            case 0xdd:          { nVirtKey = ']'; break; }
            case 0xdc:          { nVirtKey = '\\'; break; }
            case 0xba:          { nVirtKey = ';'; break; }
            case 0xde:          { nVirtKey = '\''; break; }
            case 0xbc:          { nVirtKey = ','; break; }
            case 0xbe:          { nVirtKey = '.'; break; }
            case 0xbf:          { nVirtKey = '/'; break; }
            case VK_NUMPAD0:    { nVirtKey = '0'; break; }
            case VK_NUMPAD1:    { nVirtKey = '1'; break; }
            case VK_NUMPAD2:    { nVirtKey = '2'; break; }
            case VK_NUMPAD3:    { nVirtKey = '3'; break; }
            case VK_NUMPAD4:    { nVirtKey = '4'; break; }
            case VK_NUMPAD5:    { nVirtKey = '5'; break; }
            case VK_NUMPAD6:    { nVirtKey = '6'; break; }
            case VK_NUMPAD7:    { nVirtKey = '7'; break; }
            case VK_NUMPAD8:    { nVirtKey = '8'; break; }
            case VK_NUMPAD9:    { nVirtKey = '9'; break; }
            case VK_MULTIPLY:   { nVirtKey = '*'; break; }
            case VK_DECIMAL:    { nVirtKey = '.'; break; }
            case VK_DIVIDE :    { nVirtKey = '/'; break; }
          }
          szTemp [0] = (char) nVirtKey ;
          szTemp [1] = '\0' ;
          pszChar = szTemp ;
        }
      }

      str += pszChar ;

      nVirtKey = cmHotKey.nVirtKey2 ;
      byModifiers = cmHotKey.byModifiers2 ;

      if (nVirtKey && (i == 0))
        str += ", " ;
    }
  }
  return (str) ;
}


